package br.com.programacao.concorrente.entidades;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 *A classe Buffer é responsável por ter os métodos que irão remover os itens 
 * além de popular o próprio buffer na inicialização do sistema.
 */
public class Buffer {
    
    private final Map<String, File> mapa;
    private final List<String> chaves;
    private boolean lock = false;
    private static Buffer instance = null;
    
    static {
        try {
            instance = new Buffer();
        } catch (FileNotFoundException | UnsupportedEncodingException ex) {
            Log.getLogger(Buffer.class).error(ex);
        }
    }
    /**
     * Em toda a aplicação é permitido haver apenas uma instancia do buffer.
     * E nesta instancia que todos os consumidores irão obter seus arquivos.
     * @return Instance buffer
     */
    public static Buffer getInstance() {
        return Buffer.instance;
    }
    
    /**
     * Construtor do Buffer
     * Recebe como parametro um mapa de String, File
     * @param mapa <String, File>
     */
    public Buffer(Map<String, File> mapa) {
        this.mapa = mapa;
        this.chaves = getListChaves(mapa);
    }

    /**
     * Construtor do Buffer. Durante sua contrução já é criado o mapa.
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException 
     */
    public Buffer() throws FileNotFoundException, UnsupportedEncodingException {
        this.mapa = createMapa();
        this.chaves = getListChaves(mapa);
    }

    /**
     * Cria um mapa com a quantidades de arquivos previsto na Constantes.QTD_FILES
     * Cada arquivo é preenchido com a quantidade de caracteres especificado na Constantes.QTD_CARACTERES_FILE
     * Logo após incluir o arquivo no Map, este é deletado do ficheiro.
     * @return Mapa <String,File>
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException 
     */
    private Map<String, File> createMapa() throws FileNotFoundException, UnsupportedEncodingException {
        Map<String, File> map = new HashMap<>();
        for (int i = 0; i < Constantes.QTD_FILES; i++) {
            File file = new File(String.format("%04d", i) + ".file");
            try (PrintWriter writer = new PrintWriter(file, "UTF-8")) {
                for (int j = 0; j < Constantes.QTD_CARACTERES_FILE; j++) {
                    writer.println('W');
                }
            }
            map.put(String.valueOf(i), file);
            file.delete();
        }
        return map;
    }

    /**
     * Retorna uma lista das chaves que estão presentes no mapa.
     * a partir desta lista é possivel obter uma remoção sequencial do buffer pelo
     * metodo getNext().
     * @param mapa
     * @return List<String> chaves
     */
    private List<String> getListChaves(Map<String, File> mapa) {
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, File> entry : mapa.entrySet()) {
            list.add(entry.getKey());
        }
        return list;
    }

    /**
     * Retorna o arquivo para chave, removendo o elemento do mapa(buffer).
     * Toda a remoção do buffer é dada de forma sequencial a partir da chave.
     * @param chave
     * @return File
     */
    public File getFile(String chave) {
        return mapa.remove(chave);
    }
    /**
     * Retorna a proxima chave valida do buffer.
     * @return chave
     */
    public synchronized String getNext() {
        String retorno = null;
        //if (!this.chaves.isEmpty()){
        if (!lock && this.chaves.size() > 0) {
            lock = true;
            retorno = this.chaves.remove(0);
        }
        lock = false;
        return retorno;
    }

}
